package com.idanchuang.count.core.aspect;

import com.idanchuang.component.base.JsonResult;
import com.idanchuang.count.core.annotation.UserCountUpdate;
import com.idanchuang.count.core.constant.UserCountEventTypeEnum;
import com.idanchuang.count.core.model.BizConfig;
import com.idanchuang.count.core.model.BizProperties;
import com.idanchuang.count.service.AsyncCountCommandHandler;
import com.idanchuang.count.support.UserCountChecker;
import com.idanchuang.user.count.service.api.request.command.BaseUpdateCommand;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;

/**
 * 根据异步开关 走异步kafka投递
 * 这个逻辑用于大促，降低写压力，削峰
 * 这个开关的开启时间点不是很敏感，但是关闭时间点需要在业务低峰期/不敏感的时期
 *
 * @author Richard_yyf
 * @version 1.0 2021/6/4
 */
@Component
@Aspect
@Slf4j
public class UserCountUpdateAspect {

    @Resource
    private AsyncCountCommandHandler handler;

    @Resource
    private UserCountChecker userCountChecker;

    @Resource
    private BizProperties bizProperties;

    @Around("@annotation(com.idanchuang.count.core.annotation.UserCountUpdate)")
    public Object processBeforeCountUpdate(ProceedingJoinPoint joinPoint) throws Throwable {

        BaseUpdateCommand command = (BaseUpdateCommand) joinPoint.getArgs()[0];
        BizConfig bizConfig = bizProperties.getSingleConfig(command.getBizCode());

        UserCountEventTypeEnum eventTypeEnum = ((MethodSignature) joinPoint.getSignature())
                .getMethod().getAnnotation(UserCountUpdate.class).type();

        if (eventProcessAsync(eventTypeEnum, bizConfig)) {
            // 注意 异步请求需要在listener里面做幂等校验，不能在这里设置幂等键
            handler.publishCountEvent(eventTypeEnum, command);
            return JsonResult.OK;

        } else {

            // 对于同步请求在这个地方做幂等校验
            // 异步请求在 listener 做幂等
            if (userCountChecker.checkUidExist(command.getUid(), command.getBizCode())) {
                log.info("uid[{}] 触发幂等，skip", command.getUid());
                return JsonResult.OK;
            }

            return joinPoint.proceed();
        }
    }

    private boolean eventProcessAsync(UserCountEventTypeEnum eventTypeEnum, BizConfig bizConfig) {
        // 注意，低执行频率的命令是不走异步逻辑的，会浪费topic
        // 高执行频率 && 异步开关开启 && 没有被专门配置成不走异步
        return !eventTypeEnum.isLowFrequencyOperation()
                && bizConfig.isAsyncEnabled()
                && !bizConfig.getAsyncExcludeEvents().contains(eventTypeEnum.getTag());
    }
}
